package com.kupdater.transform;

import com.sun.org.apache.bcel.internal.generic.*;
import com.sun.org.apache.bcel.internal.classfile.Method;
import com.sun.org.apache.bcel.internal.classfile.Field;
import com.kupdater.Constants;
import com.kupdater.util.TransformUtils;

/**
 * Created by IntelliJ IDEA.
 * User: Jan Ove
 * Date: 31.jan.2009
 * Time: 11:41:36
 * To change this template use File | Settings | File Templates.
 */
public class CharacterTransformer extends Transformer{
    public Class<?>[] getDependencies() {
        return new Class<?>[] { RSStringTransformer.class };
    }

    public boolean isApplicable(ClassGen cG) {
        int foundPosX = 0;
		int foundPosY = 0;
		int foundRSString = 0;
		int foundAnimation = 0;
		int foundIsMoving = 0;

		for(Field field: cG.getFields()){
			if(field.getAccessFlags() == 0 && field.getType().getSignature().equals("L"+classMap.get("RSString").getClassName()+";"))
				foundRSString++;
		}

		for(Method method:  cG.getMethods()){
			if(isAdvancedFunction1(method)){
				int count = 0;
				ConstantPoolGen cPoolGen = cG.getConstantPool();
		        MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPoolGen);
		        InstructionList iList = methodGen.getInstructionList();
				for(InstructionHandle iHandle: iList.getInstructionHandles()) {
					if(iHandle.getInstruction() instanceof GETFIELD) {
						Field field = TransformUtils.getFieldByName(cG, ((GETFIELD)iHandle.getInstruction()).getFieldName(cPoolGen));
						count++;
                        if(field == null)
                            break;

						if(field.getType() == Type.INT && field.getAccessFlags() == 0 && count == 1)
							foundAnimation++;
						else if(field.getType().getSignature().equals("[I") && field.getAccessFlags() == 0 && count == 4)
							foundPosX++;
						else if(field.getType().getSignature().equals("[I") && field.getAccessFlags() == 0 && count == 5)
							foundPosY++;
						else if(field.getType() == Type.INT && field.getAccessFlags() == 0 && count == 6){
							foundIsMoving++;
						}
					}
				}
			}
        }
		return (foundPosX == 1) && (foundPosY == 1) && (foundRSString == 1) && (foundAnimation == 1)
				&& (foundIsMoving == 1);
    }

    public boolean transform(ClassGen cG) {
        TransformUtils.classesShouldDefine++;
        String idName = this.getClass().getSimpleName().replaceAll("Transformer", "");
		String hookName = Constants.PACKAGE_HOOK + "." + idName;
		classMap.put(idName, cG);
		TransformUtils.addInterface(this, cG, hookName);
        
        boolean bReturn = hookMessage(cG) && hookLoopCycleStatusAndHeight(cG) && hookLocAni(cG);
        TransformUtils.fieldsShouldHook += 5;
        return bReturn;
    }

    boolean isAdvancedFunction1(Method method){
		Type[] description = method.getArgumentTypes();
		int intCount = 0;
		int boolCount = 0;
        int byteCount = 0;
		for(Type t: description){
			if(t == Type.INT)
				intCount++;
			if(t == Type.BOOLEAN)
				boolCount++;
            if(t == Type.BYTE)
                byteCount++;
		}
		if(intCount == 3 && boolCount == 1 && byteCount == 1 && method.getReturnType() == Type.VOID)
				return true;
		return false;
	}

    boolean hookMessage(ClassGen cG){
		for(Field field: cG.getFields()){
			if(field.getAccessFlags()== 0 && field.getType().getSignature().equals("L"+classMap.get("RSString").getClassName()+";")){
				TransformUtils.injectGetMethod(this,cG, field, "getMessage", ((Type)new ObjectType(Constants.DIRECTORY_HOOK+"/RSString")));
			}
		}
		return true;
	}

	boolean hookLoopCycleStatusAndHeight(ClassGen cG){
		for(Method method: cG.getMethods()){
			if(method.getName().equals("<init>") && method.getAccessFlags() == 0 && method.getReturnType() == Type.VOID){
				int count = 0;
                ConstantPoolGen cPoolGen = cG.getConstantPool();
		        MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPoolGen);
		        InstructionList iList = methodGen.getInstructionList();
				for(InstructionHandle iHandle: iList.getInstructionHandles()) {
					if(iHandle.getInstruction() instanceof PUTFIELD) {
						Field field = TransformUtils.getFieldByName(cG, ((PUTFIELD)iHandle.getInstruction()).getFieldName(cPoolGen));
						count++;
                        if(field == null)
                            break;

						if(field.getType() == Type.INT && field.getAccessFlags() == 0 && count == 9){
                            TransformUtils.injectGetMethod(this, cG, field, "getLoopCycleStatus");
                        }
						else if(field.getType() == Type.INT && field.getAccessFlags() == 0 && count == 20){
                            TransformUtils.injectGetMethod(this, cG, field, "getHeight");
                        }
					}
				}
				return true;
			}
		}
		return true;
	}

	boolean hookLocAni(ClassGen cG){
		for(Method method: cG.getMethods()){
			if(isAdvancedFunction1(method)){
				int countGETFIELDs = 0;
				int countPUTFIELDs = 0;
                ConstantPoolGen cPoolGen = cG.getConstantPool(); //Get the class' constantpool
		        MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPoolGen); //Create a new MethodGen (the equivilent of createing a barnd new method
		        InstructionList iList = methodGen.getInstructionList(); //Create a new list of instructions for our method

                for(InstructionHandle iHandle: iList.getInstructionHandles()){
                    if(iHandle.getInstruction() instanceof GETFIELD) {
                        countGETFIELDs++;
                        Field field = TransformUtils.getFieldByName(cG, ((GETFIELD)iHandle.getInstruction()).getFieldName(cPoolGen));
                        if(field.getType() == Type.INT && field.getAccessFlags() == 0 && countGETFIELDs == 1){
                            TransformUtils.injectGetMethod(this, cG, field, "getAnimation");
                        }
                        else if(field.getType().getSignature() == "[" && field.getAccessFlags() == 0 && countGETFIELDs == 4 ){
                            TransformUtils.injectGetMethod(this, cG, field, "getLocationY");
                        }
                        else if(field.getType().getSignature() == "[" && field.getAccessFlags() == 0 && countGETFIELDs == 5 ){
                            TransformUtils.injectGetMethod(this, cG, field, "getLocationX");
                        }
                        else if(field.getType() == Type.INT && field.getAccessFlags() == 0 && countGETFIELDs == 6){
                            TransformUtils.injectGetMethod(this, cG, field, "isMoving");
                        }
                    }
                    else if(iHandle.getInstruction() instanceof PUTFIELD){
                        countPUTFIELDs++;
                        Field field = TransformUtils.getFieldByName(cG, ((PUTFIELD)iHandle.getInstruction()).getFieldName(cPoolGen));
                        if(field.getType() == Type.INT && field.getAccessFlags() == 0 && countPUTFIELDs == 6){
                            TransformUtils.injectGetMethod(this, cG, field, "getY");
                        }
                        else if(field.getType() == Type.INT && field.getAccessFlags() == 0 && countPUTFIELDs == 7){
                            TransformUtils.injectGetMethod(this, cG, field, "getX");
                        }
                    }
                }
                return true;
            }
		}
		return false;
	}
}
